共计 8431 个字符,预计需要花费 22 分钟才能阅读完成。
一.Django 的 forms 组件应用场景
1. 应用
- 一般应用于前端的登入、注册界面, 对用户输入的字段进行校验, 快速的判断用户输入的内容是否合法, 并返回信息
2. 为何不在前端直接使用 JS 进行校验
- 前端的校验可以没有, 但后端的校验必须要有
- 因为前端的校验弱不禁风, 有很多种方式可以伪装成浏览器发送请求传递数据
- 或者通过爬虫程序绕过前端页面直接朝后端提交数据
二.forms 组件的基本使用(步骤)
- 导入 forms 组件
- 定义一个类, 并继承Form
- 在类中书写要校验的字段, 字段的属性就是要校验的规则
- 实例化得到一个 Form 对象, 把要校验的数据传入
- 调用
[form 对象].is_valid()
方法进行校验, 校验通过返回 **True - 校验通过调用
[form 对象].cleaned_data
获得校验后的数据 - 校验失败调用
[form 对象].errors
获得错误信息
三.forms 组件基本使用示例
简单的用户注册界面示例
1. 常用字段属性
属性 | 释义 |
---|---|
min_length | 最小长度 |
max_length | 最大长度 |
require | 该字段必须得填 |
label | 该字段的标签 |
2. 组件文件 form.py
-
建议在应用文件夹下创建一个专门存放 forms 组件的文件 (不创建直接写在 views.py 中也可以)
-
导入组件并创建类
from django import forms
class RegisterForm(forms.Form):
# 校验 name 字段, 最大长度为 8, 最短为 3, 下面的字段类似
name = forms.CharField(max_length=8, min_length=3, lable='用户名')
password = forms.CharField(max_length=10, min_length=4, lable='密码')
re_password = forms.CharField(max_length=10, min_length=4, lable='确认密码')
email = forms.EmailField(lable='邮箱')
3. 视图层 views.py 文件
- 实例 forms 对象, 使用对方法进行校验
from app01.myform import RegisterForm
def register(request):
if request.method == 'POST':
# 实例得到 form 对象, 将需要校验的数据传入(数据可以是前端传过来的数据, 也可以是自己后端已有的数据)
# dic = {'name':'shawn','password':'1111','re_password':'1111','email':'123@qq.com'}
# form_data = RegisterForm(dic) # 示例: 直接将已有的数据传入
form_data = RegisterForm(request.POST) # 将前端 post 请求的数据传入
# 进行校验 is_valid()
if form_data.is_valid():
print('校验成功')
# 获得校验成功的数据 cleaned_data
print(form_data.cleaned_data)
return HttpResponse('校验成功')
else:
print('检验失败')
# 获得检验失败的错误提示 errors
print(form_data.errors)
# forms 组件可以自动生成 HTML 代码,因此可以直接在页面上进行显示
form_data = RegisterForm() # 先产生 form 空对象, 再将其传给前端
return render(request, 're_form.html', {'form': form_data})
ps :
cleaned_data
属性必须是经过is_valid()
校验之后才产生的, 在校验之前使用会报错 : 属性不存在
4. 路由层 urls.py 文件
re_path('^re_form/', views.register)
5. 模板层 re_form.html 文件
<div class="container">
<div class="row">
<div class="col-md-4">
<div class="panel panel-info">
<div class="panel-heading"> 注册页面 </div>
<div class="panel-body">
<form action="" method="post">
<p> 用户名:{{form.name}}</p>
<p> 密码:{{form.password}}</p>
<p> 确认密码:{{form.re_password}}</p>
<p> 邮箱:{{form.email}}</p>
<br>
<input type="submit" class="btn btn-block btn-success" value=" 注册 ">
</form>
</div>
</div>
</div>
</div>
</div>
6. 测试效果
- 检验失败展示
- 检验成功展示
四.forms 组件之渲染标签方式
1. 自动渲染方式一
- 就是上面示例中的写法
- 可扩展性很强, 但是需要书写的代码太多
<form action="" method="post">
<p> 用户名:{{form.name}}</p>
<p> 密码:{{form.password}}</p>
<p> 确认密码:{{form.re_password}}</p>
<p> 邮箱:{{form.email}}</p>
<br>
<input type="submit" class="btn btn-block btn-success" value=" 注册 ">
</form>
- 也可以使用 label 标签替代前缀(''用户名''、''密码'' 等等)
<form action="" method="post">
<p>{{form.name.label}}:{{form.name}}</p>
<p>{{form.password.label}}:{{form.password}}</p>
<p>{{form.re_password.label}}:{{form.re_password}}</p>
<p>{{form.email.label}}:{{form.email}}</p>
<br>
<input type="submit" class="btn btn-block btn-success" value=" 注册 ">
</form>
2. 自动渲染方式二(常用方式)
- 使用 for 循环
- 代码书写简单, 并且扩展性也高 (推荐使用)
<form action="" method="post" novalidate>
{% for foo in form %}
<div class="form-group">
{# 循环取出 name,password,re_password,email #}
<p>{{foo.label}}:{{foo}}</p>
</div>
{% endfor %}
<input type="submit" class="btn btn-block btn-success" value=" 注册 ">
</form>
3. 自动渲染方式三(不常用)
- 代码书写极少, 封装程度太高, 不便于后续的扩展, 一般情况下只在本地测试使用
<form action="" method="post" novalidate>
{{form.as_p}} {# 与上两种显示方式相同 #}
{{form.as_ul}} {# 前面带点 #}
{{form.as_table}} {# 在一行显示 #}
<input type="submit" class="btn btn-block btn-success" value=" 注册 ">
</form>
五.forms 组件之渲染错误信息
1. 字段参数(举例)
- required : 是否允许为空,默认 False
- error_messages : 自定义错误信息
- widget : 使用 HTML 插件
- TextInput
- PasswordInput
2. 代码演示
- myform.py 文件
from django import forms
from django.forms import widgets
class RegisterForm(forms.Form):
# 校验 name 字段, 最大长度为 8, 最短为 3, 下面的字段类似
name = forms.CharField(max_length=8, min_length=3, label='用户名',
# 自定义错误信息
error_messages={
'min_length': '用户名最少 3 位!',
'max_length': '用户账户最大 8 位',
'required': '用户名不能为空!',
},
# 添加一个 form-control 类
widget=widgets.TextInput(attrs={'class':'form-control'}))
password = forms.CharField(max_length=10, min_length=4, label='密码',
error_messages={
'min_length': '密码最少 4 位!',
'max_length': '密码最大 10 位',
'required': '密码不能为空!',
},
widget=widgets.PasswordInput(attrs={'class':'form-control'}))
re_password = forms.CharField(max_length=10, min_length=4, label='确认密码',
error_messages={
'min_length': '密码最少 4 位!',
'max_length': '密码最大 10 位',
'required': '密码不能为空!',
},
widget=widgets.PasswordInput(attrs={'class':'form-control'}))
email = forms.EmailField(label='邮箱',
error_messages={
'invalid': '邮箱格式不正确',
'required': '邮箱不能为空',
},
widget=widgets.TextInput(attrs={'class':'form-control'}))
- views.py 文件
from app01.myform import RegisterForm
def register(request):
if request.method == 'POST':
form_data = RegisterForm(request.POST) # 将前端 post 请求的数据传入
if form_data.is_valid():
print(form_data.cleaned_data)
return HttpResponse('校验成功')
else:
# 将带有错误信息的 form 对象返回前端页面
return render(request, 're_form.html', {'form': form_data})
form_data = RegisterForm() # 将 form 对象传给前端
return render(request, 're_form.html', {'form': form_data})
- re_form.html 文件
<form action="" method="post" novalidate>
{% for foo in form %}
{# 在后方设置一个 span 标签用来放错误提示 #}
<p>{{foo.label}}:{{foo}}
<span style="color: red">{{foo.errors.0}}</span>
</p>
{% endfor %}
<input type="submit" class="btn btn-block btn-success" value=" 注册 ">
</form>
ps :
<form action=""method="post" novalidate>
: form 表单标签内的novalidate
表示前端不进行校验
3. 效果展示
六.forms 组件之全局钩子, 局部钩子
1. 钩子函数
- 全局和局部钩子都是定义在 forms 组件之内的函数
- 钩子函数类似于校验的第二道关卡, 能够让我们自定义校验规则
2. 两种钩子函数
- 局部钩子 : 用于给单个字段增加校验规则
- 全局钩子 : 用于给多个字段增加校验规则
3. 两种钩子函数的书写方式
- 局部钩子
def clean_字段名(self):
字段值 = self.cleaned_data.get('字段名')
[判断逻辑代码]
[不通过抛出 ValidationError 异常]
[通过则返回该字段值]
- 全局钩子
def clean(self):
字段值 1 = self.cleaned_data.get('字段 1')
字段值 2 = self.cleaned_data.get('字段 2')
[判断逻辑代码]
[不通过抛出 ValidationError 异常]
[通过则 return self.cleaned_data]
4. 钩子示例
- 需求一 : 校验用户名不能有敏感字眼
- 需求二 : 校验两次输入的密码需一致
myform.py 新增钩子函数
# 局部钩子
def clean_name(self):
name = self.cleaned_data.get('name')
for i in ['爱德华','sb']:
if i in name:
raise ValidationError(f'用户名中包含敏感词汇:"{i}"')
else:
return name
# 全局钩子
def clean(self):
password = self.cleaned_data.get('password')
re_password = self.cleaned_data.get('re_password')
if password == re_password:
return self.cleaned_data
else:
raise ValidationError('两次密码不一致')
views.py 文件
from app01.myform import RegisterForm
def register(request):
if request.method == 'POST':
form_data = RegisterForm(request.POST) # 将前端 post 请求的数据传入
if form_data.is_valid():
return HttpResponse('校验成功')
else:
# 获取到 ValidationError 中的 error 信息传给前端
error = form_data.errors.get('__all__')
return render(request, 're_form.html', {'form': form_data,'error':error})
form_data = RegisterForm() # 将 form 空对象传给前端
return render(request, 're_form.html', {'form': form_data})
re_form.html 文件
<form action="" method="post" novalidate>
{% for foo in form %}
<p>{{foo.label}}:{{foo}}
<span style="color: red; display:inline-block">{{foo.errors.0}}</span>
</p>
{% endfor %}
<input type="submit" class="btn btn-block btn-success" value=" 注册 ">
{# 判断错误信息是否为 None, 是 None 就不渲染 error #}
{% if error is None %}
<span style="color: red; display:inline-block"></span>
{% else %}
<span style="color: red; display:inline-block">{{error}}</span>
{% endif %}
</form>
需求一演示 :
需求二演示 :
七.error 错误提示为何是 ul 标签分析
- 上面我们在 views.py 文件中写到的, 教研室表打印错误提示信息
else:
print('检验失败')
# 获得检验失败的错误提示 errors
print(form_data.errors)
- 查看打印结果 :
为什么是一个
ul
标签格式, 一定是重写了__str__
方法
- 我们导入 ErrorDict 来查看内部源码
from django.forms.utils import ErrorDict
发现其内部重写了
__str__
方法, 返回的是as_ul
方法, 该方法内部就是定义了 html 的格式并返回并且还有一些其他的方法 : 比如 json 格式, data 数据, text 格式等等
而返回
as_ul
方法则是为了方便 渲染模板
八. 常用字段及插件
1. 初始值 : initial
username = forms.CharField(
min_length=8,
label=" 用户名 ",
initial="shawn" # 设置默认值
)
2. 自定义错误提示 : error_massages
username = forms.CharField(
min_length=8,
label=" 用户名 ",
initial="shawn",
error_messages={
"required": " 不能为空 ",
"invalid": " 格式错误 ",
"min_length": " 用户名最短 8 位 "
}
)
3. 密码框 : password
# 设置 render_value 在输入错误的时候, 保留输入的内容 value 值
pwd = forms.CharField(
min_length=4,
label=" 密码 ",
widget=forms.widgets.PasswordInput(attrs={'class': 'a1'}, render_value=True)
)
4. 反选 radio
gender = forms.fields.ChoiceField(choices=((1, " 男 "), (2, " 女 "), (3, " 未知 ")),
label=" 性别 ",
initial=3,
widget=forms.widgets.RadioSelect())
5. 单选 checkbox
keep = forms.ChoiceField(
label=" 是否记住密码 ",
initial="checked",
widget=forms.widgets.CheckboxInput())
6. 多选 checkbox
Copyhobbies1 = forms.MultipleChoiceField(choices=((1, "read"), (2, "run"), (3, "play"),),
label=" 爱好 ",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple())
7. 单选 Select
Copyeducation = forms.ChoiceField(choices=((1, " 高中 "), (2, " 中专 "), (3, " 大专 "), (4, " 本科 "), (5, " 研究生 "), (6, " 硕士 ")),
label=" 学历 ",
initial=3,
widget=forms.widgets.Select(attrs={'class': 'form-control'})
)
8. 多选 Select
Copyhobbies2 = forms.MultipleChoiceField(choices=((1, " 篮球 "), (2, " 足球 "), (3, " 双色球 "),),
label=" 爱好 ",
initial=[1, 2],
widget=forms.widgets.SelectMultiple(attrs={'class': 'form-control'})
)
9. 更多插件(了解)
插件名称 | 对应 input |
---|---|
TextInput | input type="text" |
PasswordInput | input type="password" |
HiddenInput | input type="hidden" |
NumberInput | input type="number" |
EmailInput | input type="email" |
URLInput | input type="url" |
Textarea | textarea |
DateInput | input type="data" |
DateTimeInput | input type="datetime" |
TimeInput | 只获取时分秒 |
CheckboxInput | input type="checkbox" |
CheckboxSelectMultiple | 多选按钮 |
Select | select 框 |
NullBooleanSelect | select 框 三个选项 0 1 null |
SelectMultiple | 可多选的 select 框 |
RadioSelect | input type="radion" |
FileInput | 可以查看当前目录下的所有文件。作用不大 |
正文完